package proxy

import (
	system "awesomeProxy/system/common"
	"awesomeProxy/system/helper"
	TcpServer "awesomeProxy/system/tcpServer/tcpServer"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"strconv"
	"sync"
	"time"
)

type proxyClientConn struct {
	uniqueId string
	conn     net.Conn
}

type ProxyServer struct {
	conChan                chan proxyClientConn
	WorkConnections        sync.Map
	ControlConnections     sync.Map
	connectionIsInit       sync.Map
	ProxyConnAndWornConMap sync.Map
	ProxyMap               sync.Map
	ProxyMapConnection     sync.Map
	ConnectionRelation     sync.Map
}

func (proxyServer *ProxyServer) GetProxyConn(uniqueId string) (proxyClientConn, error) {
	var controlCon interface{}
	var ok bool
	if controlCon, ok = proxyServer.ControlConnections.Load(uniqueId); !ok {
		return proxyClientConn{}, errors.New("目标未能正常连接到代理节点:" + uniqueId)
	}
	failTime := 0
	if workChan, ok := proxyServer.WorkConnections.Load(uniqueId); ok {
		timer := time.NewTicker(time.Millisecond * 100)
		for true {
			select {
			case con := <-workChan.(chan proxyClientConn):
				if _, ok := proxyServer.connectionIsInit.Load(con.conn); !ok {
					fmt.Println("获取到已经断开的连接:retry")
					continue
				}
				return con, nil
			case <-timer.C:
				failTime += 1
				if failTime > 5 {
					timer.Stop()
					return proxyClientConn{}, errors.New("获取工作节点失败")
				}
				proxyServer.AskClientCreateNewProxyCon(controlCon.(net.Conn))
			}
		}
	} else {
		return proxyClientConn{}, errors.New("目标未能正常连接到代理节点:" + uniqueId)
	}
	return proxyClientConn{}, errors.New("获取工作节点失败")
}
func (proxyServer *ProxyServer) AskClientCreateNewProxyCon(conn net.Conn) {
	res := ResponseMsg{
		Action: ACTION_CREATE_WORK,
	}
	data, err := system.JsonEncode(res)
	if err == nil {
		conn.Write([]byte(data + SPLITE_CHAR))
	}

}

func (proxyServer *ProxyServer) saveWorkConnections() {
	for true {
		proxyCon := <-proxyServer.conChan
		if proxyWorkConns, ok := proxyServer.WorkConnections.Load(proxyCon.uniqueId); ok {
			proxyWorkConns.(chan proxyClientConn) <- proxyCon
		} else {
			proxyWorkConnChan := make(chan proxyClientConn, 100)
			proxyWorkConnChan <- proxyClientConn{
				conn:     proxyCon.conn,
				uniqueId: proxyCon.uniqueId,
			}
			proxyServer.WorkConnections.Store(proxyCon.uniqueId, proxyWorkConnChan)
		}
	}
}
func (proxyServer *ProxyServer) StartServer(port int) {
	server := TcpServer.TcpServer{}
	server.OnStart(func() {
		proxyServer.conChan = make(chan proxyClientConn)
		go proxyServer.saveWorkConnections()
		go func() {
			for true {
				fmt.Println("开始输出工作节点")
				proxyServer.ConnectionRelation.Range(func(key, value interface{}) bool {
					fmt.Println(value.(ConnectionRelation).SourceCon.RemoteAddr(), "远程连接")
					fmt.Println(value.(ConnectionRelation).TargetCon.RemoteAddr(), "目标连接")
					return true
				})
				time.Sleep(time.Second * 3)
			}
		}()
	})
	server.OnMessage(func(connection net.Conn, data []byte) {
		//if workCon, ok := proxyServer.ProxyMap.Load(connection); ok {
		//	fmt.Println("接受到请求数据")
		//	workCon.(chan []byte) <- data
		//	return
		//}
		if connectionRelation, ok := proxyServer.ConnectionRelation.Load(connection.RemoteAddr()); ok {
			fmt.Println("接受到请求数据")
			relation := connectionRelation.(ConnectionRelation)
			err := relation.SendToOtherConnection(connection, data)
			if err != nil {
				fmt.Println(err, "发送异常")
				return
			}
			return
		}
		_, err := system.HttpUnPackage(string(data))
		if err == nil {
			//httpParams := httpInfo.QueryParams()
			//if uniqueId, ok := httpParams["unique_id"]; ok {
			//	connection.Write([]byte(system.WriteHttpCookieRes(uniqueId, 60*60*24*30)))
			//	return
			//}
			//uniqueId := "ac34d26aadeae3eb51fe9d678b582c74"
			//CookieUniqueId := httpInfo.GetCookie(system.COOKIE_UNIQUE_ID)
			CookieUniqueId := "ac34d26aadeae3eb51fe9d678b582c74"
			if CookieUniqueId != "" {
				connectionRelation := ConnectionRelation{
					SourceCon: connection,
				}
				con, err := proxyServer.GetProxyConn(CookieUniqueId)
				connectionRelation.UniqueId = CookieUniqueId
				connectionRelation.ProxyServer = proxyServer
				if err != nil {
					connection.Write([]byte(err.Error()))
					fmt.Println(err)
					connection.Close()
				} else {
					connectionRelation.TargetCon = con.conn
					proxyServer.ConnectionRelation.Store(connection.RemoteAddr(), connectionRelation)
					proxyServer.ConnectionRelation.Store(con.conn.RemoteAddr(), connectionRelation)
					connectionRelation.SendToOtherConnection(connection, data)
				}
			} else {
				fmt.Println("无法解析到目标节点")
				connection.Write([]byte("无法解析到目标节点"))
				connection.Close()
			}
			return
		}
		if _, ok := proxyServer.connectionIsInit.Load(connection); !ok {
			controlMsgObj := ControlMsg{}
			err := json.Unmarshal(data, &controlMsgObj)
			if err != nil {
				connection.Close()
			}
			switch controlMsgObj.Action {
			case ACTION_CONTROL:
				proxyServer.connectionIsInit.Store(connection, true)
				proxyServer.ControlConnections.Store(controlMsgObj.UniqueId, connection)
				proxyServer.AskClientCreateNewProxyCon(connection)
				fmt.Println("代理客户端成功连接到服务器访问方式:" + "http://host:" + strconv.Itoa(port) + "/?unique_id=" + controlMsgObj.UniqueId)
				break
			case ACTION_TRANSFORM:
				fmt.Println("创建一条新的转发连接" + helper.Now())
				proxyServer.connectionIsInit.Store(connection, true)
				proxyServer.conChan <- proxyClientConn{
					conn:     connection,
					uniqueId: controlMsgObj.UniqueId,
				}
			default:
				fmt.Errorf("undefinde action closed type:" + strconv.Itoa(controlMsgObj.Action))
			}

		} else {
			if proxyServer.WorkConnections.Load(connection); ok {

			}
		}
	})
	server.OnConnect(func(connection net.Conn) {
		conn := connection.(*net.TCPConn)
		SetKeepAlive(conn)
	})
	server.OnDisConnect(func(connection net.Conn) {
		//return
		//释放资源
		fmt.Println("释放资源")
		if _, ok := proxyServer.ProxyMap.Load(connection); ok {
			proxyServer.ProxyMap.Delete(connection)
		}
		//移除控制节点
		proxyServer.ControlConnections.Range(func(key, value interface{}) bool {
			if value == connection {
				proxyServer.ControlConnections.Delete(key)
			}
			return true
		})
		if _, ok := proxyServer.connectionIsInit.Load(connection); ok {
			proxyServer.connectionIsInit.Delete(connection)
		}
		if conR, ok := proxyServer.ConnectionRelation.Load(connection.RemoteAddr()); ok {
			relation := conR.(ConnectionRelation)
			con, err := relation.GetToOtherConnection(connection)
			if err == nil {
				con.Close()
				proxyServer.ConnectionRelation.Delete(con.RemoteAddr())
			}
			proxyServer.ConnectionRelation.Delete(connection.RemoteAddr())

		}

	})
	server.Start(port, false)
}
